package cn.chendd.core;

import com.alibaba.fastjson.JSONObject;

import java.io.*;
import java.util.*;

import javax.swing.JOptionPane;
/**
 * 按指定文件大小分割文件,并记录文件分割信息
 * @author chendd
 *
 */
public class SplitFile {

    private long blockLength;//分割单个文件大小，参与运算
    private long currentTotal;//当前分割次数的位置
    private Map<String , Object> propMap;//记录本次分割文件的信息
    private boolean order;//排序，true为顺序写入，false为随机写入
    
    public SplitFile(long blockLength , boolean order) {
        this.blockLength = blockLength;
        propMap = new HashMap<String , Object>();
        this.order = order;
    }

    public boolean split(File srcFile , String splitFolder) throws Exception {
        RandomAccessFile raf = null;
        try {
            long fileLens = srcFile.length();//文件大小
            if(fileLens <= this.blockLength){
                //System.out.println("分割的文件大小 " + this.blockLength + " 大于文件的大小!");
            	JOptionPane.showMessageDialog(null, "设置要分割的文件大小 " + (this.blockLength / 1024 / 1024) + "M 大于文件的大小!", 
            			"警告", JOptionPane.ERROR_MESSAGE);
                return false;
            }
            raf = new RandomAccessFile(srcFile, "r");
            //根据分割块大小，计算一共有多少块文件
            int blockCount = (int) (fileLens % this.blockLength);
            if(blockCount == 0){
                blockCount = (int) (fileLens / this.blockLength);
            }else{
                blockCount = (int) (fileLens / this.blockLength) + 1;
            }
            //按快进行读取文件
            String srcFileName = srcFile.getName();
            //记录源文件名称
            propMap.put("fileName" , srcFileName);//原始文件名称
            propMap.put("fileLength" , this.getFileLength(srcFile));
            propMap.put("fileSuffix" , this.getFileSuffix(srcFileName));
            List<String> partList = new ArrayList<String>();
            int max = 0;
            for(int i=0 ; i < blockCount ; i++){
            	int label;
            	if(order){
            		label = i + 1;
            	} else {
            		//不考虑会出现重复的
            		label = (10000 + (int)(Math.random() * 10000));
            	}
            	if (label > max) {
            	    max = label;
                }
                File destFile = new File(splitFolder + File.separator + srcFileName + "." + label + ".part");
                String partFileName = destFile.getName();
                partList.add(partFileName);
                splitFileDetail(destFile , raf);//分割文件
            }
            //重命名最后一个文件，在最大值的基础上再增加一个万以内的随机数字，约定最后一个文件的编号值为最大
            File lastFile = renameLastFile(max , srcFile , partList);
            propMap.put("partList" , partList);//碎片文件列表
            //记录还原文件相关的数据,用序列化文件
            writePropFile(propMap , srcFile , lastFile);
            return true;
        } catch (Exception e) {
           throw new Exception(e);
        } finally{
            if(raf != null){
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }

    /**
     * 最大文件编号
     * @param max 最大编号
     * @param partList 文件列表
     */
    private File renameLastFile(int max , File srcFile , List<String> partList) {
        String fileName = partList.get(partList.size() - 1);
        File file = new File(srcFile , fileName);
        //如果是随机顺序的文件则修改最后一个文件的名称为最大编号
        if (order) {
            return file;
        }
        String label = (max + new Random().nextInt(10000)) + "";
        String baseName = fileName.substring(0 , fileName.indexOf("."));
        File lastFile = new File(file.getParentFile() , baseName + "." + label + ".part");
        file.renameTo(lastFile);
        partList.set(partList.size() - 1 , lastFile.getAbsolutePath());
        return lastFile;
    }

    /**
     * 将记录的文件相关的数据写入文件
     * @param propMap 写入文件属性对象
     * @param lastFile 源文件名称
     */
    private void writePropFile(Map<String, Object> propMap , File srcFile , File lastFile)
    	throws Exception{
        System.out.println(lastFile.getAbsolutePath());
        String json = JSONObject.toJSONString(propMap);
        RandomAccessFile raf = new RandomAccessFile(lastFile , "rw");
        raf.seek(0);
        raf.writeChars(json);
        raf.close();
    }

    private void splitFileDetail(File destFile , RandomAccessFile raf) throws Exception {
        byte b[] = new byte[1024 * 4];
        int lens = 0;
        //如果文件目录不存在，则创建
        if(! destFile.getParentFile().exists()){
            destFile.getParentFile().mkdirs();
        }
        BufferedOutputStream bos = null;
        try {
            raf.seek(currentTotal);//设置开始读取的位置
            long currentMax = raf.getFilePointer() + this.blockLength;
            bos = new BufferedOutputStream(new FileOutputStream(destFile));
            while ((lens = raf.read(b)) != -1) {
                //判断文件读取的大小，避免已经读取超过每块的大小了
                if (currentTotal + lens > currentMax) {
                    break;
                }
                bos.write(b, 0, lens);
                currentTotal += lens;
            }
        } catch (Exception e) {
            throw new Exception(e);
        }finally{
            if(bos != null){
                try {
                    bos.flush();
                    bos.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
        }
    }

    /**
     * 根据文件名称获取文件后缀
     * @param fileName 文件名称
     * @return 返回.zip或.exe等
     */
    private String getFileSuffix(String fileName){
        int index = fileName.lastIndexOf(".");
        if(index == -1){
            return "";
        }
        return fileName.substring(index);
    }
    
    /**
     * 获取文件大小
     * @param srcFile 源文件
     * @return 文件大小long类型
     */
    private long getFileLength(File srcFile){
        return srcFile.length();
    }
    
}
